package com.zy.common.swagger;


import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.zy.common.swagger.properies.SwaggerProperties;
import com.zy.common.swagger.properies.ApiVersion;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;

@Configuration
@EnableOpenApi
@EnableConfigurationProperties({SwaggerProperties.class})
@ConditionalOnProperty(
        prefix = "swagger",
        value = {"enable"},
        havingValue = "true"
)
@Slf4j
public class SwaggerConfig implements BeanFactoryAware, WebMvcConfigurer {
    public DefaultListableBeanFactory listableBeanFactory;
    private static final String split = ";";
    @Autowired
    private SwaggerProperties swaggerProperties;
    public static final String DEFAULT = "default1";

    public SwaggerConfig() {
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(new String[]{"/swagger-ui.html"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/"});
        registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"});
        registry.addResourceHandler(new String[]{"/static/**"}).addResourceLocations(new String[]{"classpath:/static/"});
    }

    @PostConstruct
    public void init() {
        List<String> groupVersions = this.swaggerProperties.getGroupVersions();
        if (groupVersions != null && !groupVersions.isEmpty()) {
            for (int i = 0; i < groupVersions.size(); ++i) {
                String groupVersion = (String) groupVersions.get(i);
                Docket docket = this.createRestApi(groupVersion);
                this.listableBeanFactory.registerSingleton("docker_" + i, docket);
            }

        }
    }

    @Bean
    public Docket docketDefault() {
        return this.createRestApi("");
    }

    private Docket createRestApi(String version) {
        log.debug("====================" + this.swaggerProperties.getScanBasePackage());
        return (new Docket(DocumentationType.OAS_30)).enable(this.swaggerProperties.getEnable()).apiInfo(this.apiInfo(StringUtils.isEmpty(version) ? "default" : version)).groupName(StringUtils.isEmpty(version) ? "default" : version).select().apis(basePackage(this.swaggerProperties.getScanBasePackage())).apis((input) -> {
            if (StringUtils.isEmpty(version)) {
                return true;
            } else {
                HandlerMethod requestHandler = input.getHandlerMethod();
                ApiVersion classAnonotaionApiVersion = (ApiVersion) requestHandler.getBeanType().getAnnotation(ApiVersion.class);
                ApiVersion methodAnnotationApiVersion = (ApiVersion) requestHandler.getMethodAnnotation(ApiVersion.class);
                if (methodAnnotationApiVersion != null) {
                    return Arrays.asList(methodAnnotationApiVersion.group()).contains(version);
                } else {
                    return classAnonotaionApiVersion != null ? Arrays.asList(classAnonotaionApiVersion.group()).contains(version) : false;
                }
            }
        }).paths(PathSelectors.any()).build();
    }

    private ApiInfo apiInfo(String version) {
        return (new ApiInfoBuilder()).title(this.swaggerProperties.getTitle()).description(this.swaggerProperties.getDescription()).version(version).build();
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        this.listableBeanFactory = listableBeanFactory;
    }

    public static Predicate<RequestHandler> basePackage(String basePackage) {
        return (input) -> {
            return (Boolean) declaringClass(input).transform(handlerPackage(basePackage)).or(true);
        };
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }

    private static Function<Class<?>, Boolean> handlerPackage(String basePackage) {
        return (input) -> {
            String[] var2 = basePackage.split(";");
            int var3 = var2.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                String strPackage = var2[var4];
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }

            return false;
        };
    }
}